package typejump.objects;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import typejump.engine.Animation;

public class Sparkles extends GameObject {

	private List<Sparklet> sparklets;
	private double defsize = 10;

	public static final int VEL_ALLOW_POS = 1;
	public static final int VEL_ALLOW_NEG = 2;
	public static final int VEL_ALLOW_BOTH = VEL_ALLOW_POS | VEL_ALLOW_NEG;
	
	private double accelX = 0;
	private double accelY = 0;
	
	private int alsx = VEL_ALLOW_BOTH;
	private int alsy = VEL_ALLOW_BOTH;
	
	protected double xrange, yrange, xspeedmin, xspeedmax, yspeedmin, yspeedmax;
	private double spawnRate = 10; // Sparkles per second
	
	private double defscale = 1;
	
	protected double sparkleLife = 0.5;
	
	private Animation animation;
	
	private boolean stopped = false;
	
	private double timeout = 0;
	private boolean timesout = false;
	
	public Sparkles(
			double xrange, double yrange,
			double xspeedmin, double xspeedmax,
			double yspeedmin, double yspeedmax, 
			double spawnRate) {
		sparklets = new CopyOnWriteArrayList<Sparklet>();
		this.xrange = xrange;
		this.yrange = yrange;
		
		this.xspeedmin = xspeedmin;
		this.xspeedmax = xspeedmax;
		this.yspeedmin = yspeedmin;
		this.yspeedmax = yspeedmax;
		
		this.spawnRate = spawnRate;
		
		width(0);
		height(0);
	}
	
	public void setAcceleration(double x, double y) {
		this.accelX = x;
		this.accelY = y;
	}
	
	public void setVelRuleX(int vel) {
		this.alsx = vel;
	}
	public void setVelRuleY(int vel) {
		this.alsy = vel;
	}
	
	public Sparkles(double xrange, double yrange, double spawnRate) {
		this(xrange, yrange, 0, 0, 0, 0, spawnRate);
	}
	
	public Sparkles(double xrange, double yrange) {
		this(xrange, yrange, 30);
	}
	
	public void addSparkles(int numsparkles) {
		while (numsparkles-- > 0)
			sparkle();
	}
	
	public void setTimeout(double timeout) {
		this.timeout = timeout;
		this.timesout = timeout >= 0;
	}
	
	public void setRangeX(double range) {
		this.xrange = range;
	}
	public void setRangeY(double range) {
		this.yrange = range;
	}
	
	public void stopSparkling() {
		this.stopped = true;
	}
	
	/**
	 * Sets how long sparkles take to die
	 * @param lifetime
	 */
	public void setLifetime(double lifetime) {
		if (lifetime > 0)
			this.sparkleLife = lifetime;
	}
	
	public void setAnimation(Animation anim) {
		this.animation = anim;
	}
	
	public void setScale(double scale) {
		this.defscale = scale;
	}
	
	/**
	 * Average number of sparkles per second
	 * @param rate
	 */
	public void setSpawnRate(double rate) {
		this.spawnRate = rate;
	}
	
	protected double getScale() {
		return defscale;
	}
	
	protected Sparklet sparkle() {
		double posx = centerX() + xrange * (Math.random()-0.5);
		double posy = centerY() + yrange * (Math.random()-0.5);
		
		Sparklet sparkle = new Sparklet(posx, posy);
		sparkle.scale = this.defscale;
		sparkle.vx = (Math.random()*2-1) * (xspeedmax - xspeedmin) + xspeedmin;
		sparkle.vy = (Math.random()*2-1) * (yspeedmax - yspeedmin) + yspeedmin;

		if (sparkle.vx < 0 && (alsx & VEL_ALLOW_NEG) == 0) sparkle.vx = -sparkle.vx;
		if (sparkle.vx > 0 && (alsx & VEL_ALLOW_POS) == 0) sparkle.vx = -sparkle.vx;
		
		if (sparkle.vy < 0 && (alsy & VEL_ALLOW_NEG) == 0) sparkle.vy = -sparkle.vy;
		if (sparkle.vy > 0 && (alsy & VEL_ALLOW_POS) == 0) sparkle.vy = -sparkle.vy;
		
		sparklets.add(sparkle);
		return sparkle;
	}
	
	public void setSpeedX(double min, double max) {
		this.xspeedmin = min;
		this.xspeedmax = max;
	}
	public void setSpeedY(double min, double max) {
		this.yspeedmin = min;
		this.yspeedmax = max;
	}
	
	
	@Override
	public boolean paintEnabled() {
		// TODO Auto-generated method stub
		return true;
	}

	private BufferedImage img;
	@Override
	public void paint(Graphics2D g) {
		g.setColor(Color.BLUE);
		for (Sparklet spark : sparklets) {
			if (spark.isDead()) continue;
			
			img = null;
			if (animation != null)
				img = animation.getImage(spark.time);
			
			if (img == null) {
				g.drawOval(
						(int)(-centerX() + spark.x - spark.scale*defsize/2),
						(int)(-centerY() + spark.y - spark.scale*defsize/2), 
						(int)(spark.scale*defsize), 
						(int)(spark.scale*defsize));
			} else {
				g.drawImage(img,
						(int)(-centerX() + spark.x - spark.scale*img.getWidth()/2),
						(int)(-centerY() + spark.y - spark.scale*img.getHeight()/2), 
						(int)(spark.scale*img.getWidth()), 
						(int)(spark.scale*img.getHeight()),
						null);
			}
		}
	}
	
	protected void updateSparklet(Sparklet spark, double time) {
		spark.time += time;
		spark.vx += accelX * time;
		spark.vy += accelY * time;
		spark.x += spark.vx * time;
		spark.y += spark.vy * time;
		spark.shrink(time);
		if (spark.isDead()) {
			dead.add(spark);
		}
	}

	private LinkedList<Sparklet> dead = new LinkedList<Sparklet>();
	@Override
	public void update(double time) {
		timeout -= time;
		if (timesout && timeout <= 0)
			stopSparkling();
		
		dead.clear();
		for (Sparklet spark : sparklets) {
			updateSparklet(spark, time);
		}
		while (dead.size() > 0)
			sparklets.remove(dead.pop());
		
		if (!stopped && Math.random() < time*spawnRate)
			sparkle();
		
		if (stopped && sparklets.size() == 0) {
			remove();
		}
	}
	
	
	class Sparklet {
		double x, y, vx, vy, scale, time;
		public Sparklet(double x, double y) {
			this.x = x;
			this.y = y;
			this.scale = 1;
			time = 0;
		}
		public void shrink(double time) {
			this.scale -= defscale * time / sparkleLife;
		}
		public boolean isDead() {
			return this.scale <= 0;
		}
	}

}
